From 77b5aee5c5f01a7c4e9a0e2c9a5616f9a026605f Mon Sep 17 00:00:00 2001 From: "kaf24@scramble.cl.cam.ac.uk" Date: Fri, 26 Mar 2004 15:17:37 +0000 Subject: [PATCH] bitkeeper revision 1.825.5.1 (40644991zRxZVjAd23Dlw69CHQg32A) irq.h, interrupt.h, console.c, physdev.c, event_channel.c, irq.c, io_apic.c: More support in Xen for binding IRQs to guests. Also changed console redirect sequence to triple-ctrl-g -- ctrl-a is a useful editing command. --- xen/arch/i386/io_apic.c | 25 +++--- xen/arch/i386/irq.c | 126 ++++++++++++++++++++++++++- xen/common/event_channel.c | 23 +++-- xen/common/physdev.c | 161 +---------------------------------- xen/drivers/char/console.c | 22 ++--- xen/include/asm-i386/irq.h | 6 +- xen/include/asm-x86_64/irq.h | 6 +- xen/include/xen/interrupt.h | 3 +- xen/include/xen/irq.h | 49 +++++------ 9 files changed, 197 insertions(+), 224 deletions(-) diff --git a/xen/arch/i386/io_apic.c b/xen/arch/i386/io_apic.c index c4a2e48535..96c8b9a54e 100644 --- a/xen/arch/i386/io_apic.c +++ b/xen/arch/i386/io_apic.c @@ -138,14 +138,10 @@ static void __init replace_pin_at_irq(unsigned int irq, static void name##_IO_APIC_irq (unsigned int irq) \ __DO_ACTION(R, ACTION, FINAL) -DO_ACTION( __mask, 0, |= 0x00010000, io_apic_sync(entry->apic) ) - /* mask = 1 */ -DO_ACTION( __unmask, 0, &= 0xfffeffff, ) - /* mask = 0 */ -DO_ACTION( __mask_and_edge, 0, = (reg & 0xffff7fff) | 0x00010000, ) - /* mask = 1, trigger = 0 */ -DO_ACTION( __unmask_and_level, 0, = (reg & 0xfffeffff) | 0x00008000, ) - /* mask = 0, trigger = 1 */ +DO_ACTION( __mask, 0, |= 0x00010000, io_apic_sync(entry->apic) ) +DO_ACTION( __unmask, 0, &= 0xfffeffff, ) +DO_ACTION( __edge, 0, &= 0xffff7fff, ) +DO_ACTION( __level, 0, |= 0x00008000, ) static void mask_IO_APIC_irq (unsigned int irq) { @@ -1365,13 +1361,15 @@ static unsigned int startup_level_ioapic_irq (unsigned int irq) return 0; /* don't check for pending */ } -static void end_level_ioapic_irq (unsigned int irq) +static void mask_and_ack_level_ioapic_irq(unsigned int irq) { unsigned long v; int i; balance_irq(irq); + mask_IO_APIC_irq(irq); + /* * It appears there is an erratum which affects at least version 0x11 * of I/O APIC (that's the 82093AA and cores integrated into various @@ -1405,7 +1403,7 @@ static void end_level_ioapic_irq (unsigned int irq) atomic_inc(&irq_mis_count); #endif spin_lock(&ioapic_lock); - __mask_and_edge_IO_APIC_irq(irq); + __edge_IO_APIC_irq(irq); #ifdef APIC_LOCKUP_DEBUG for (entry = irq_2_pin + irq;;) { unsigned int reg; @@ -1421,12 +1419,15 @@ static void end_level_ioapic_irq (unsigned int irq) entry = irq_2_pin + entry->next; } #endif - __unmask_and_level_IO_APIC_irq(irq); + __level_IO_APIC_irq(irq); spin_unlock(&ioapic_lock); } } -static void mask_and_ack_level_ioapic_irq (unsigned int irq) { /* nothing */ } +static void end_level_ioapic_irq(unsigned int irq) +{ + unmask_IO_APIC_irq(irq); +} static inline void init_IO_APIC_traps(void) { diff --git a/xen/arch/i386/irq.c b/xen/arch/i386/irq.c index 6b45b02080..19cf5c7dc4 100644 --- a/xen/arch/i386/irq.c +++ b/xen/arch/i386/irq.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -66,6 +67,8 @@ irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL }; #endif +static void __do_IRQ_guest(int irq); + /* * Special irq handlers. */ @@ -333,7 +336,9 @@ void __global_restore_flags(unsigned long flags) * waste of time and is not what some drivers would * prefer. */ -int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * action) +static int handle_IRQ_event(unsigned int irq, + struct pt_regs * regs, + struct irqaction * action) { int status; int cpu = smp_processor_id(); @@ -483,6 +488,7 @@ asmlinkage unsigned int do_IRQ(struct pt_regs regs) spin_lock(&desc->lock); desc->handler->ack(irq); + /* REPLAY is when Linux resends an IRQ that was dropped earlier WAITING is used by probe to mark irqs that are being tested @@ -490,6 +496,14 @@ asmlinkage unsigned int do_IRQ(struct pt_regs regs) status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); status |= IRQ_PENDING; /* we _want_ to handle it */ + /* We hook off guest-bound IRQs for special handling. */ + if ( status & IRQ_GUEST ) + { + __do_IRQ_guest(irq); + spin_unlock(&desc->lock); + return 1; + } + /* * If the IRQ is disabled for whatever reason, we cannot use the action we * have. @@ -883,6 +897,13 @@ int setup_irq(unsigned int irq, struct irqaction * new) * The following block of code has to be executed atomically */ spin_lock_irqsave(&desc->lock,flags); + + if ( desc->status & IRQ_GUEST ) + { + spin_unlock_irqrestore(&desc->lock,flags); + return -EBUSY; + } + p = &desc->action; if ((old = *p) != NULL) { /* Can't share interrupts unless both agree to */ @@ -906,7 +927,110 @@ int setup_irq(unsigned int irq, struct irqaction * new) desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING); desc->handler->startup(irq); } + spin_unlock_irqrestore(&desc->lock,flags); return 0; } + + + +/* + * HANDLING OF GUEST-BOUND PHYSICAL IRQS + */ + +#define IRQ_MAX_GUESTS 7 +typedef struct { + unsigned int nr_guests; + struct task_struct *guest[IRQ_MAX_GUESTS]; +} irq_guest_action_t; + +static void __do_IRQ_guest(int irq) +{ + irq_desc_t *desc = &irq_desc[irq]; + irq_guest_action_t *action = (irq_guest_action_t *)desc->action; + struct task_struct *p; + int i; + + for ( i = 0; i < action->nr_guests; i++ ) + { + p = action->guest[i]; + send_guest_pirq(p, irq); + } +} + +int pirq_guest_bind(struct task_struct *p, int irq) +{ + unsigned long flags; + irq_desc_t *desc = &irq_desc[irq]; + irq_guest_action_t *action; + int rc; + + if ( !IS_PRIV(p) ) + return -EPERM; + + spin_lock_irqsave(&desc->lock, flags); + + if ( !(desc->status & IRQ_GUEST) ) + { + rc = -EBUSY; + if ( desc->action != NULL ) + goto out; + + rc = -ENOMEM; + action = kmalloc(sizeof(irq_guest_action_t), GFP_KERNEL); + if ( (desc->action = (struct irqaction *)action) == NULL ) + goto out; + + action->nr_guests = 0; + + desc->depth = 0; + desc->status |= IRQ_GUEST; + desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING); + desc->handler->startup(irq); + } + + action = (irq_guest_action_t *)desc->action; + + rc = -EBUSY; + if ( action->nr_guests == IRQ_MAX_GUESTS ) + goto out; + + action->guest[action->nr_guests++] = p; + + out: + spin_unlock_irqrestore(&desc->lock, flags); + return rc; +} + +int pirq_guest_unbind(struct task_struct *p, int irq) +{ + unsigned long flags; + irq_desc_t *desc = &irq_desc[irq]; + irq_guest_action_t *action; + int i; + + spin_lock_irqsave(&desc->lock, flags); + + action = (irq_guest_action_t *)desc->action; + + if ( action->nr_guests == 1 ) + { + desc->action = NULL; + kfree(action); + desc->status |= IRQ_DISABLED; + desc->status &= ~IRQ_GUEST; + desc->handler->shutdown(irq); + } + else + { + i = 0; + while ( action->guest[i] != p ) + i++; + memmove(&action->guest[i], &action->guest[i+1], IRQ_MAX_GUESTS-i-1); + action->nr_guests--; + } + + spin_unlock_irqrestore(&desc->lock, flags); + return 0; +} diff --git a/xen/common/event_channel.c b/xen/common/event_channel.c index d0c0f5bba0..d68e28b553 100644 --- a/xen/common/event_channel.c +++ b/xen/common/event_channel.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -181,27 +182,32 @@ static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind) { struct task_struct *p = current; int pirq = bind->pirq; - int port; + int port, rc; if ( pirq >= ARRAY_SIZE(p->pirq_to_evtchn) ) return -EINVAL; spin_lock(&p->event_channel_lock); - if ( ((port = p->pirq_to_evtchn[pirq]) != 0) || - ((port = get_free_port(p)) < 0) ) + if ( ((rc = port = p->pirq_to_evtchn[pirq]) != 0) || + ((rc = port = get_free_port(p)) < 0) ) goto out; + p->pirq_to_evtchn[pirq] = port; + if ( (rc = pirq_guest_bind(p, pirq)) != 0 ) + { + p->pirq_to_evtchn[pirq] = 0; + goto out; + } + p->event_channel[port].state = ECS_PIRQ; p->event_channel[port].u.pirq = pirq; - p->pirq_to_evtchn[pirq] = port; - out: spin_unlock(&p->event_channel_lock); - if ( port < 0 ) - return port; + if ( rc < 0 ) + return rc; bind->port = port; return 0; @@ -237,7 +243,8 @@ static long __evtchn_close(struct task_struct *p1, int port1) break; case ECS_PIRQ: - p1->pirq_to_evtchn[chn1[port1].u.pirq] = 0; + if ( (rc = pirq_guest_unbind(p1, chn1[port1].u.pirq)) == 0 ) + p1->pirq_to_evtchn[chn1[port1].u.pirq] = 0; break; case ECS_VIRQ: diff --git a/xen/common/physdev.c b/xen/common/physdev.c index 08da1b5206..cc2f21b172 100644 --- a/xen/common/physdev.c +++ b/xen/common/physdev.c @@ -65,26 +65,16 @@ /* bit offsets into state */ #define ST_BASE_ADDRESS 0 /* bits 0-5: are for base address access */ #define ST_ROM_ADDRESS 6 /* bit 6: is for rom address access */ -#define ST_IRQ_DELIVERED 7 /* bit 7: waiting for end irq call */ -typedef struct _phys_dev_st -{ +typedef struct _phys_dev_st { int flags; /* flags for access etc */ struct pci_dev *dev; /* the device */ struct list_head node; /* link to the list */ struct task_struct *owner; /* 'owner of this device' */ int state; /* state for various checks */ - - hw_irq_controller *new_handler; /* saved old handler */ - hw_irq_controller *orig_handler; /* saved old handler */ - } phys_dev_t; -#define MAX_IRQS 32 -/* an array of device descriptors index by IRQ number */ -static phys_dev_t *irqs[MAX_IRQS]; - /* * * General functions @@ -573,158 +563,9 @@ static long pci_find_irq(int seg, int bus, int dev, int func, u32 *val) return 0; } -static void phys_dev_interrupt(int irq, void *dev_id, struct pt_regs *ptregs) -{ - phys_dev_t *pdev; - - if ( (pdev = (phys_dev_t *)dev_id) == NULL ) - { - printk("spurious interrupt, no proper device id, %d\n", irq); - return; - } - - /* XXX KAF: introduced race here? */ - set_bit(ST_IRQ_DELIVERED, &pdev->state); - send_guest_pirq(pdev->owner, irq); -} - -/* this is called instead of the PICs original end handler. - * the real end handler is only called once the guest signalled the handling - * of the event. */ -static void end_virt_irq (unsigned int i) -{ - /* nothing */ -} - -/* - * a guest request an IRQ from a device to be routed to it - * - shared interrupts are not allowed for now - * - we change the hw_irq handler to something else - */ -static long pirq_request(int irq) -{ - int err; - phys_dev_t *pdev = NULL, *t; - hw_irq_controller *new, *orig; - struct list_head *tmp; - - printk("request irq %d\n", irq); - - /* find pdev */ - - list_for_each(tmp, ¤t->pcidev_list) - { - t = list_entry(tmp, phys_dev_t, node); - if ( t->dev->irq == irq ) - { - pdev = t; - break; - } - } - - if ( pdev == NULL ) - { - printk("no device matching IRQ %d\n", irq); - return -EINVAL; - } - - if ( irq >= MAX_IRQS ) - { - printk("requested IRQ to big %d\n", irq); - return -EINVAL; - } - - if ( irqs[irq] != NULL ) - { - printk ("irq already in use %d\n", irq); - return -EPERM; - } - - /* allocate a hw_irq controller and copy the original */ - if ( !(new = kmalloc(sizeof(hw_irq_controller), GFP_KERNEL)) ) - { - printf("error allocating new irq controller\n"); - return -ENOMEM; - } - orig = irq_desc[irq].handler; - new->typename = orig->typename; - new->startup = orig->startup; - new->shutdown = orig->shutdown; - new->enable = orig->enable; - new->disable = orig->disable; - new->ack = orig->ack; - new->end = orig->end; - new->set_affinity = orig->set_affinity; - - /* swap the end routine */ - new->end = end_virt_irq; - - /* change the irq controllers */ - pdev->orig_handler = orig; - pdev->new_handler = new; - irq_desc[irq].handler = new; - irqs[irq] = pdev; - - printk ("setup handler %d\n", irq); - - /* request the IRQ. this is not shared and we use a slow handler! */ - err = request_irq(irq, phys_dev_interrupt, SA_INTERRUPT, - "foo", (void *)pdev); - if ( err ) - { - printk("error requesting irq\n"); - /* restore original */ - irq_desc[irq].handler = pdev->orig_handler; - /* free memory */ - kfree(new); - return err; - } - - printk ("done\n"); - - return 0; -} - -long pirq_free(int irq) -{ - phys_dev_t *pdev; - - if ( irq >= MAX_IRQS ) - { - printk("requested IRQ to big %d\n", irq); - return -EINVAL; - } - - if ( irqs[irq] == NULL ) - { - printk ("irq not used %d\n", irq); - return -EINVAL; - } - - pdev = irqs[irq]; - - /* shutdown IRQ */ - free_irq(irq, (void *)pdev); - - /* restore irq controller */ - irq_desc[irq].handler = pdev->orig_handler; - - /* clean up */ - pdev->orig_handler = NULL; - irqs[irq] = NULL; - kfree(pdev->new_handler); - pdev->new_handler = NULL; - - printk("freed irq %d", irq); - return 0; -} static long pci_unmask_irq(void) { -#if 0 - clear_bit(ST_IRQ_DELIVERED, &pdev->state); - pdev->orig_handler->end(irq); -#endif return 0; } diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c index d0fe21acb0..2eeb70cf89 100644 --- a/xen/drivers/char/console.c +++ b/xen/drivers/char/console.c @@ -227,8 +227,8 @@ long read_console_ring(unsigned long str, unsigned int count, unsigned cmd) static char serial_rx_ring[SERIAL_RX_SIZE]; static unsigned int serial_rx_cons, serial_rx_prod; -/* CTRL-a switches input direction between Xen and DOM0. */ -#define CTRL_A 0x01 +/* CTRL-g switches input direction between Xen and DOM0. */ +#define CTRL_G 0x07 static int xen_rx = 1; /* FALSE => serial input passed to domain 0. */ static void switch_serial_input(void) @@ -236,7 +236,7 @@ static void switch_serial_input(void) static char *input_str[2] = { "DOM0", "Xen" }; xen_rx = !xen_rx; printk("*** Serial input -> %s " - "(type 'CTRL-a' three times to switch input to %s).\n", + "(type 'CTRL-g' three times to switch input to %s).\n", input_str[xen_rx], input_str[!xen_rx]); } @@ -264,22 +264,22 @@ static void __serial_rx(unsigned char c, struct pt_regs *regs) static void serial_rx(unsigned char c, struct pt_regs *regs) { - static int ctrl_a_count = 0; + static int ctrl_g_count = 0; - if ( c == CTRL_A ) + if ( c == CTRL_G ) { - /* We eat CTRL-a in groups of three to switch console input. */ - if ( ++ctrl_a_count == 3 ) + /* We eat CTRL-g in groups of three to switch console input. */ + if ( ++ctrl_g_count == 3 ) { switch_serial_input(); - ctrl_a_count = 0; + ctrl_g_count = 0; } } else { - /* Flush any pending CTRL-a's. They weren't for us. */ - for ( ; ctrl_a_count != 0; ctrl_a_count-- ) - __serial_rx(CTRL_A, regs); + /* Flush any pending CTRL-b's. They weren't for us. */ + for ( ; ctrl_g_count != 0; ctrl_g_count-- ) + __serial_rx(CTRL_G, regs); /* Finally process the just-received character. */ __serial_rx(c, regs); } diff --git a/xen/include/asm-i386/irq.h b/xen/include/asm-i386/irq.h index cf1094bf0f..2c7c67a0da 100644 --- a/xen/include/asm-i386/irq.h +++ b/xen/include/asm-i386/irq.h @@ -192,10 +192,10 @@ extern unsigned long prof_shift; #include -#ifdef CONFIG_SMP /*more of this file should probably be ifdefed SMP */ +#if defined(CONFIG_X86_IO_APIC) static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) { - if (IO_APIC_IRQ(i)) - send_IPI_self(IO_APIC_VECTOR(i)); + if (IO_APIC_IRQ(i)) + send_IPI_self(IO_APIC_VECTOR(i)); } #else static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) {} diff --git a/xen/include/asm-x86_64/irq.h b/xen/include/asm-x86_64/irq.h index 74e0495555..bbb83c2d95 100644 --- a/xen/include/asm-x86_64/irq.h +++ b/xen/include/asm-x86_64/irq.h @@ -124,10 +124,10 @@ extern unsigned long prof_shift; #include -#ifdef CONFIG_SMP /*more of this file should probably be ifdefed SMP */ +#if defined(CONFIG_X86_IO_APIC) static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) { - if (IO_APIC_IRQ(i)) - send_IPI_self(IO_APIC_VECTOR(i)); + if (IO_APIC_IRQ(i)) + send_IPI_self(IO_APIC_VECTOR(i)); } #else static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) {} diff --git a/xen/include/xen/interrupt.h b/xen/include/xen/interrupt.h index f9f2762d9b..a55e70269f 100644 --- a/xen/include/xen/interrupt.h +++ b/xen/include/xen/interrupt.h @@ -16,7 +16,7 @@ typedef void irqreturn_t; #define IRQ_NONE #define IRQ_HANDLED #define IRQ_RETVAL(x) - + struct irqaction { void (*handler)(int, void *, struct pt_regs *); unsigned long flags; @@ -26,7 +26,6 @@ struct irqaction { struct irqaction *next; }; - enum { TIMER_BH = 0, SCSI_BH diff --git a/xen/include/xen/irq.h b/xen/include/xen/irq.h index f4d81bec79..169c4170cb 100644 --- a/xen/include/xen/irq.h +++ b/xen/include/xen/irq.h @@ -1,5 +1,5 @@ -#ifndef __irq_h -#define __irq_h +#ifndef __XEN_IRQ_H__ +#define __XEN_IRQ_H__ #include #include @@ -14,26 +14,24 @@ #define IRQ_REPLAY 8 /* IRQ has been replayed but not acked yet */ #define IRQ_AUTODETECT 16 /* IRQ is being autodetected */ #define IRQ_WAITING 32 /* IRQ not yet seen - for autodetection */ -#define IRQ_LEVEL 64 /* IRQ level triggered */ -#define IRQ_MASKED 128 /* IRQ masked - shouldn't be seen again */ -#define IRQ_PER_CPU 256 /* IRQ is per CPU */ +#define IRQ_GUEST 64 /* IRQ is handled by guest OS(es) */ /* * Interrupt controller descriptor. This is all we need * to describe about the low-level hardware. */ struct hw_interrupt_type { - const char * typename; - unsigned int (*startup)(unsigned int irq); - void (*shutdown)(unsigned int irq); - void (*enable)(unsigned int irq); - void (*disable)(unsigned int irq); - void (*ack)(unsigned int irq); - void (*end)(unsigned int irq); - void (*set_affinity)(unsigned int irq, unsigned long mask); + const char *typename; + unsigned int (*startup)(unsigned int irq); + void (*shutdown)(unsigned int irq); + void (*enable)(unsigned int irq); + void (*disable)(unsigned int irq); + void (*ack)(unsigned int irq); + void (*end)(unsigned int irq); + void (*set_affinity)(unsigned int irq, unsigned long mask); }; -typedef struct hw_interrupt_type hw_irq_controller; +typedef struct hw_interrupt_type hw_irq_controller; #include @@ -45,19 +43,22 @@ typedef struct hw_interrupt_type hw_irq_controller; * Pad this out to 32 bytes for cache and indexing reasons. */ typedef struct { - unsigned int status; /* IRQ status */ - hw_irq_controller *handler; - struct irqaction *action; /* IRQ action list */ - unsigned int depth; /* nested irq disables */ - spinlock_t lock; + unsigned int status; /* IRQ status */ + hw_irq_controller *handler; + struct irqaction *action; /* IRQ action list */ + unsigned int depth; /* nested irq disables */ + spinlock_t lock; } ____cacheline_aligned irq_desc_t; -extern irq_desc_t irq_desc [NR_IRQS]; +extern irq_desc_t irq_desc[NR_IRQS]; -extern int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); -extern int setup_irq(unsigned int , struct irqaction * ); +extern int setup_irq(unsigned int, struct irqaction *); -extern hw_irq_controller no_irq_type; /* needed in every arch ? */ +extern hw_irq_controller no_irq_type; extern void no_action(int cpl, void *dev_id, struct pt_regs *regs); -#endif /* __asm_h */ +struct task_struct; +extern int pirq_guest_bind(struct task_struct *p, int irq); +extern int pirq_guest_unbind(struct task_struct *p, int irq); + +#endif /* __XEN_IRQ_H__ */ -- 2.30.2